/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.nio.channels.spi;

import java.io.IOException;
import java.nio.channels.*;


/**
 * 可选通道的基本实现类。
 *
 * <p> 此类定义处理通道注册、取消注册和关闭的机制的方法。
 *  它保持该通道当前的阻塞模式及其当前的选择键的集合。
 *  它执行了SelectableChannel规范的所有同步。
 *  此类中定义的抽象保护方法的实现不需要与可能参与相同操作的其他线程同步。</p>
 *
 *
 * @author Mark Reinhold
 * @author Mike McCloskey
 * @author JSR-51 Expert Group
 * @since 1.4
 */

public abstract class AbstractSelectableChannel
    extends SelectableChannel
{

    // 创建这个通道的提供者
    private final SelectorProvider provider;


    // 通过向选择器注册此频道而创建的密钥。它们之所以被保存，是因为如果这个通道被关闭，密钥必须被注销。由钥匙锁保护。
    private SelectionKey[] keys = null;
    private int keyCount = 0;

    // Lock for key set and count
    private final Object keyLock = new Object();

    // Lock for registration and configureBlocking operations
    private final Object regLock = new Object();

    // Blocking mode, protected by regLock
    boolean blocking = true;

    /**
     * 初始化此类的新实例。
     *
     * @param  provider
     *         The provider that created this channel
     */
    protected AbstractSelectableChannel(SelectorProvider provider) {
        this.provider = provider;
    }

    /**
     * 返回创建此频道的提供程序。
     *
     * @return  The provider that created this channel
     */
    public final SelectorProvider provider() {
        return provider;
    }


    // -- Utility methods for the key set --

    private void addKey(SelectionKey k) {
    	// 确保当前线程持有锁 keyLock
        assert Thread.holdsLock(keyLock);
        int i = 0;
        if ((keys != null) && (keyCount < keys.length)) {
            // 找到key数组的空元素
            for (i = 0; i < keys.length; i++)
                if (keys[i] == null)
                    break;
        } else if (keys == null) {
        	// 如果keys为null，初始化一个容量为3的数组给keys
            keys =  new SelectionKey[3];
        } else {
        	// 此处，keys不为null，而且容量已满，扩大key数组到2倍
            int n = keys.length * 2;
            SelectionKey[] ks =  new SelectionKey[n];
            // 复制原数组到ks，然后ks赋值给keys
            for (i = 0; i < keys.length; i++)
                ks[i] = keys[i];
            keys = ks;
            i = keyCount;
        }
        // k赋值给keys[i]
        keys[i] = k;
        keyCount++;
    }

    private SelectionKey findKey(Selector sel) {
        synchronized (keyLock) {
            if (keys == null)
                return null;
            // 对keys数组进行遍历查找
            for (int i = 0; i < keys.length; i++)
                if ((keys[i] != null) && (keys[i].selector() == sel))
                    return keys[i];
            return null;
        }
    }

    // 先对keys数组进行遍历查找，然后直接keys[i] = null，不对其余元素操作（add时是找到第一个空的位子！！！）
    void removeKey(SelectionKey k) {                    // package-private
        synchronized (keyLock) {
            for (int i = 0; i < keys.length; i++)
                if (keys[i] == k) {
                    keys[i] = null;
                    keyCount--;
                }
            ((AbstractSelectionKey)k).invalidate();
        }
    }

    private boolean haveValidKeys() {
        synchronized (keyLock) {
            if (keyCount == 0)
                return false;
            for (int i = 0; i < keys.length; i++) {
            	// 判断是否有元素，并且key还有效
                if ((keys[i] != null) && keys[i].isValid())
                    return true;
            }
            return false;
        }
    }


    // -- Registration --

    public final boolean isRegistered() {
        synchronized (keyLock) {
        	// 判断keyCount是否为0
            return keyCount != 0;
        }
    }

    public final SelectionKey keyFor(Selector sel) {
        return findKey(sel);
    }

    /**
     * 用给定的选择器注册此通道，返回一个选择键。
     *
     * <p>  该方法首先验证该通道是开放的，并且给定的初始兴趣集是有效的。
     *
     * <p>如果此频道已使用给定选择器注册，
     * 则在将其兴趣设置为给定值后，将返回表示该注册的选择键。
     *
     * <p> 否则，这个通道还没有注册到给定的选择器，
     * 所以选择器的register方法在保持适当锁的同时被调用。
     * 在返回之前，返回的密钥被添加到此通道的密钥集中。
     * </p>
     *
     * @throws  ClosedSelectorException {@inheritDoc}
     *
     * @throws  IllegalBlockingModeException {@inheritDoc}
     *
     * @throws  IllegalSelectorException {@inheritDoc}
     *
     * @throws  CancelledKeyException {@inheritDoc}
     *
     * @throws  IllegalArgumentException {@inheritDoc}
     */
    public final SelectionKey register(Selector sel, int ops,
                                       Object att)
        throws ClosedChannelException
    {
        synchronized (regLock) {
            if (!isOpen())
                throw new ClosedChannelException();
            if ((ops & ~validOps()) != 0)
                throw new IllegalArgumentException();
            if (blocking)
                throw new IllegalBlockingModeException();
            SelectionKey k = findKey(sel);
            if (k != null) {
            	// 如果已经注册了，设置ops和att
                k.interestOps(ops);
                k.attach(att);
            }
            if (k == null) {
                // 新注册，以keyLock同步
                synchronized (keyLock) {
                    if (!isOpen())
                        throw new ClosedChannelException();
                    // 调用selector的register方法，然后调用channel的addKey方法
                    k = ((AbstractSelector)sel).register(this, ops, att);
                    addKey(k);
                }
            }
            return k;
        }
    }


    // -- Closing --

    /**
     * 关闭此频道。
     *
     * <p> 此方法在AbstractInterruptableChannel类中指定，并由close方法调用，
     * 然后调用implCloseSelectableChannel方法以执行关闭此通道的实际工作。
     * 然后取消该频道的所有键。</p>
     */
    protected final void implCloseChannel() throws IOException {
        implCloseSelectableChannel();
        synchronized (keyLock) {
            int count = (keys == null) ? 0 : keys.length;
            for (int i = 0; i < count; i++) {
                SelectionKey k = keys[i];
                // 调用每个SelectionKey的cancel方法
                if (k != null)
                    k.cancel();
            }
        }
    }

    /**
     * 关闭此可选通道。
     *
     * <p> close方法调用此方法以执行关闭通道的实际工作。
     * 此方法仅在通道尚未关闭的情况下调用，并且不能多次调用。
     *
     * <p> 此方法的实现必须，通过引发异常或正常返回，
     * 来安排在此通道上的I/O操作中阻塞的任何其他线程，立即返回。
     * </p>
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    protected abstract void implCloseSelectableChannel() throws IOException;


    // -- Blocking --

    public final boolean isBlocking() {
        synchronized (regLock) {
            return blocking;
        }
    }

    public final Object blockingLock() {
        return regLock;
    }

    /**
     * 调整此频道的阻塞模式。
     *
     * <p> 如果给定的阻塞模式与当前的阻塞模式不同，
     * 则此方法调用ImpleConfigureBlocking方法，
     * 同时保持适当的锁，以便更改模式。</p>
     */
    public final SelectableChannel configureBlocking(boolean block)
        throws IOException
    {
        synchronized (regLock) {
            if (!isOpen())
                throw new ClosedChannelException();
            if (blocking == block)
                return this;
            if (block && haveValidKeys())
                throw new IllegalBlockingModeException();
            implConfigureBlocking(block);
            blocking = block;
        }
        return this;
    }

    /**
     * 调整此频道的阻塞模式。
     *
     * <p> configureBlocking方法调用此方法以执行更改阻塞模式的实际工作。
     * 此方法仅在新模式与当前模式不同时调用。</p>
     *
     * @param  block  If <tt>true</tt> then this channel will be placed in
     *                blocking mode; if <tt>false</tt> then it will be placed
     *                non-blocking mode
     *
     * @throws IOException
     *         If an I/O error occurs
     */
    protected abstract void implConfigureBlocking(boolean block)
        throws IOException;

}
